﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once
#include "GcmpModelInterface.h"

namespace gcmp
{
    class IModelView;
    class IDocument;

    /// \brief 执行消隐的类型
    ///
    enum class HideEdgeType : int32_t
    {
        UnKnown,        ///< 未知
        Join,           ///< 连接
        Cut,            ///< 扣减
        Custom          ///< 自定义
    };
    
    /// \brief 视图级消隐中两个Element的可见性设置，表示消隐时Element的消隐边是否可见
    enum class HideEdgeVisibilityPriority : int32_t
    {
        Default = 0,        ///< 默认逻辑，参与消隐的两个Element的消隐边均不可见
        First = 1,          ///< 第一个Element的消隐边可见，第二个Element的消隐边不可见
        Second = 2          ///< 第一个Element的消隐边不可见，第二个Element的消隐边可见
    };


    /// \brief消隐条件判断接口类
    class GCMP_MODEL_INTERFACE_EXPORT IHideEdgeCondition
    {
    public:
        virtual ~IHideEdgeCondition() {}

    public:

        /// \brief 是否允许两个Element在指定视图进行消隐的操作
        ///
        /// 当有多个判断条件时，只要有一个返回了false，则这两个Element不允许消隐
        /// \param pModelView 当前在处理消隐的视图
        /// \param firstElementId 参与消隐的第一个Element的Id
        /// \param secondElementId 参与消隐的第二个Element的Id
        /// \param hideEdgeType 消隐的类型
        /// \param visibilityPriority 参与消隐的两个Element的消隐边是否可见
        /// \return true，允许消隐；false，不允许消隐
        virtual bool AllowHideEdgeInModelView(const IModelView* pModelView, ElementId firstElementId, ElementId secondElementId, HideEdgeType hideEdgeType, HideEdgeVisibilityPriority& visibilityPriority) const = 0;

        /// \brief 是否允许两个Element在图形表达中进行消隐的操作
        ///
        /// 当有多个判断条件时，只要有一个返回了false，则这两个Element不允许消隐
        /// \param pDocument Element所属的文档
        /// \param firstElementId 参与消隐的第一个Element的Id
        /// \param secondElementId 参与消隐的第二个Element的Id
        /// \param hideEdgeType 消隐的类型
        /// \param visibilityPriority 参与消隐的两个Element的消隐边是否可见
        /// \return true，允许消隐；false，不允许消隐
        virtual bool AllowHideEdgeInModelShape(const IDocument* pDocument, ElementId firstElementId, ElementId secondElementId, HideEdgeType hideEdgeType, HideEdgeVisibilityPriority& visibilityPriority) const = 0;

    };

    /// \brief 轮廓线消隐操作管理类接口
    class GCMP_MODEL_INTERFACE_EXPORT IHideEdgesManager
    {
    public:
        virtual ~IHideEdgesManager() {}

    public:

        /// \brief 获取消隐操作管理类
        /// 
        /// \return 消隐操作管理类
        static IHideEdgesManager* Get();


        /// \brief 注册消隐判断条件
        /// 
        /// \param conditionName 消隐判断条件的名称
        /// \param opHideEdgeCondition 消隐判断条件
        /// \return true，注册成功；false，注册失败
        virtual bool RegisterHideEdgeCondition(const std::wstring& conditionName, OwnerPtr<IHideEdgeCondition> opHideEdgeCondition) = 0;


        /// \brief 取消注册消隐判断条件
        /// 
        /// \param contidionName 消隐判断条件的名称
        /// \return true，取消注册成功；false，取消注册失败
        virtual bool UnRegisterHideEdgeCondition(const std::wstring& contidionName) = 0;


        /// \brief 获得所有已注册的消隐条件判断
        /// 
        /// \return 所有已注册的消隐条件判断
        virtual std::vector<const IHideEdgeCondition*> GetAllHideEdgeConditions() const = 0;


        /// \brief 更新文档内Element对象消隐边（一般用于消隐条件变化，对象没有变化主动触发更新消隐）
        /// 
        /// \param pDocument hideEdgeElements所属的文档
        /// \param hideEdgeElements 需要更新消隐流程的对象集
        /// \return true，更新成功；false，更新失败
        virtual bool UpdateElementHideEdge(IDocument* pDocument, const std::set<ElementId>& hideEdgeElements) = 0;

        /// \brief 添加文档内Element对象用于消隐边（一般用于消隐条件变化，对象没有变化主动触发更新消隐）
        /// 
        /// 区别：UpdateElementHideEdge——会对参与消隐的对象集做Grep重建，场景较大时会有性能问题
        ///       AddElementHideEdge——不会去做消隐前Grep重建
        ///
        /// \param pDocument hideEdgeElements所属的文档
        /// \param hideEdgeElements 需要添加到消隐流程的对象集
        /// \return true，添加成功；false，添加失败
        virtual bool AddElementHideEdge(IDocument* pDocument, const std::set<ElementId>& hideEdgeElements) = 0;

        /// \brief 设置用于消隐判断的角度容差(用于判断重合边关联的面是否共面)
        ///  当两个关联面的弧度大于该容差时，不执行消隐操作
        ///  当设置的容差超过 Pi ，角度容差将被设置为 pi ，设置的容差低于 0 ，角度容差将被设置为 0
        ///  设置后，不会自动刷新已经完成消隐操作的构件，需要再次刷新构件才能够正常消隐
        /// \param angleEpsilon     角度容差(弧度)，范围0到Pi，默认0.01745329252（1度）
        /// \return void
        virtual void SetHideEdgeAngleEpsilon(double angleEpsilon) = 0;

        /// \brief 获取用于消隐判断的角度容差(用于判断重合边关联的面是否共面)
        ///
        /// \return double 角度容差
        virtual double GetHideEdgeAngleEpsilon() const = 0;
    };
}

namespace gcmp
{
    template<class HideEdgeConditionClassName>
    class GcmpHideEdgeConditionRegisterHelper
    {
        std::wstring m_conditionName;
    public:
        GcmpHideEdgeConditionRegisterHelper(const std::wstring & conditionName)
        {
            auto opHideEdgeCondition = NEW_AS_OWNER_PTR(HideEdgeConditionClassName);
            m_conditionName = conditionName;
            if (IHideEdgesManager::Get())
            {
                IHideEdgesManager::Get()->RegisterHideEdgeCondition(conditionName, TransferOwnership(opHideEdgeCondition));
            }
            else {
                DBG_WARN(L"IHideEdgesManager为空", L"GMEP", L"20201101");
            }

        }

        ~GcmpHideEdgeConditionRegisterHelper()
        {
            if (IHideEdgesManager::Get())
            {
                IHideEdgesManager::Get()->UnRegisterHideEdgeCondition(m_conditionName);
            }
            else {
                DBG_WARN(L"IHideEdgesManager为空", L"GMEP", L"20201101");
            }
        }
    };
}

/// \brief 生成宽字符宏
#define CONVERTTOWSTRING(A,B) A##B

/// \brief 消隐条件判断注册宏
/// 
/// 如果使用该宏，消隐条件会在模块卸载时自动取消注册；如需提前手动取消注册，请使用类名进行取消注册操作。
#define REGISTER_HIDEEDGE_CONDITION(HIDEEDGE_CONDITION_NAME) \
namespace { \
    gcmp::GcmpHideEdgeConditionRegisterHelper<HIDEEDGE_CONDITION_NAME> s_##HIDEEDGE_CONDITION_NAME##HideEdgeConditionRegisterHelper(CONVERTTOWSTRING(L,#HIDEEDGE_CONDITION_NAME)); \
}
